package org.twnmayberry.web;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.twndmv.dto.PersonDTO;
import org.twndmv.ejb.DmvTestUtilRemote;
import org.twndmv.ejb.PersonMgmtRemote;
import org.twndmv.ejb.VehicleMgmtRemote;
import org.twnmayberry.bo.Sex;
import org.twnmayberry.bo.Type;
import org.twnmayberry.dto.ActivityDTO;
import org.twnmayberry.dto.POIDTO;
import org.twnmayberry.ejb.MayberryTestUtilRemote;
import org.twnmayberry.ejb.PoiMgmtRemote;

public class MayberryController extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static final Log log = LogFactory.getLog(MayberryController.class);
	private static final String UNKNOWN_COMMAND_URL = "/WEB-INF/mayberry/UnknownCommand.jsp";
	protected static final String DISPLAY_EXCEPTION_URL = "/WEB-INF/mayberry/DisplayException.jsp";
	protected static final String DISPLAY_RESULT_URL = "/WEB-INF/mayberry/DisplayResult.jsp";
	public static final String ADMIN_TYPE = "admin";
	public static final String ANONYMOUS_TYPE = "anonymous";
	public static final String PING_COMMAND = "Ping";
	public static final String EXCEPTION_PARAM = "exception";
	public static final String COMMAND_PARAM = "command";
	public static final String HANDLER_TYPE_KEY = "type";

	protected static final String RESULT_PARAM = "result";

	// commands
	public static final String CREATE_POI_COMMAND = "Create POI";
	public static final String GET_ALL_POIS_COMMAND = "Get All POIs";
	public static final String GET_POI_COMMAND = "Get POI";
	public static final String RETRIEVE_POIS_COMMAND = "Retrieve POIs";
	public static final String POPULATE_DB_COMMAND = "Populate DB";
	public static final String RESET_DB_COMMAND = "Reset DB";
	public static final String ADD_ACTIVITY_COMMAND = "Add Act";
	public static final String CHECK_DANGER_COMMAND = "Check Danger";
	public static final String ADD_DMV_PEEP_COMMAND = "Add DMV Person";

	private Map<String, GetHandler> getHandlers = new HashMap<String, GetHandler>();
	private Map<String, PostHandler> postHandlers = new HashMap<String, PostHandler>();

	@EJB
	private PoiMgmtRemote poiManager;

	@EJB
	private MayberryTestUtilRemote testUtil;

	@EJB(lookup = "ejb:twnDmvEAR/twnDmvEJB/VehicleMgmtEJB!org.twndmv.ejb.VehicleMgmtRemote")
	private VehicleMgmtRemote carManager;

	@EJB(lookup = "ejb:twnDmvEAR/twnDmvEJB/PersonMgmtEJB!org.twndmv.ejb.PersonMgmtRemote")
	private PersonMgmtRemote personManager;
	
	@EJB(lookup = "ejb:twnDmvEAR/twnDmvEJB/DmvTestUtilEJB!org.twndmv.ejb.DmvTestUtilRemote")
	private DmvTestUtilRemote dmvTestUtil;

	public void init() throws ServletException {
		log.debug("init() called ");
		try {
			ServletConfig config = getServletConfig();
			initEJBs(config);

			// build a list of handlers for individual commands
			// String handlerType = config.getInitParameter(HANDLER_TYPE_KEY);
			getHandlers.put(GET_ALL_POIS_COMMAND, new GetAllPOIs());
			getHandlers.put(GET_POI_COMMAND, new GetPOI());
			getHandlers.put(RETRIEVE_POIS_COMMAND, new RetrievePOIs());
			getHandlers.put(CREATE_POI_COMMAND, new CreatePOI());
			getHandlers.put(POPULATE_DB_COMMAND, new PopulateDB());
			getHandlers.put(RESET_DB_COMMAND, new ResetDB());
			getHandlers.put(ADD_ACTIVITY_COMMAND, new AddAct());
			getHandlers.put(CHECK_DANGER_COMMAND, new CheckDanger());
			getHandlers.put(ADD_DMV_PEEP_COMMAND, new AddDmvPeep());

			postHandlers.put(CREATE_POI_COMMAND, new CreatePOIPost());
			postHandlers.put(ADD_ACTIVITY_COMMAND, new AddActPost());
			postHandlers.put(CHECK_DANGER_COMMAND, new CheckDangerPost());
			postHandlers.put(ADD_DMV_PEEP_COMMAND, new AddDmvPeepPost());

			// log.debug("configured handler type:" + handlerType +
			// " with " + handlers);
		} catch (Exception ex) {
			log.fatal("error initializing GetHandler", ex);
			throw new ServletException("error initializing GetHandler", ex);
		}
	}

	private void initEJBs(ServletConfig config) throws Exception {
		log.debug("initEJBs()");
		// build an InitialContext from Servlet.init properties in web.xml
		InitialContext jndi = null;
		String ctxFactory = config.getServletContext().getInitParameter(
				Context.INITIAL_CONTEXT_FACTORY);
		log.debug(Context.INITIAL_CONTEXT_FACTORY + "=" + ctxFactory);
		if (ctxFactory != null) {
			Properties env = new Properties();
			env.put(Context.INITIAL_CONTEXT_FACTORY, ctxFactory);
			jndi = new InitialContext(env);
		} else {
			jndi = new InitialContext();
		}

		if (personManager == null) {
			String jndiName = config.getServletContext().getInitParameter(
					"person.remote");
			personManager = (PersonMgmtRemote) jndi.lookup(jndiName);
			log.debug("personManager initialized:" + personManager);
		}
		if (carManager == null) {
			String jndiName = config.getServletContext().getInitParameter(
					"vehicle.remote");
			carManager = (VehicleMgmtRemote) jndi.lookup(jndiName);
			log.debug("carManager initialized:" + carManager);

		}
		if (testUtil == null) {
			String jndiName = config.getServletContext().getInitParameter(
					"testutil.remote");
			testUtil = (MayberryTestUtilRemote) jndi.lookup(jndiName);
			log.debug("testUtil initialized:" + testUtil);
		} 
		if (poiManager == null) {
			String jndiName = config.getServletContext().getInitParameter(
					"poi.remote");
			poiManager = (PoiMgmtRemote) jndi.lookup(jndiName);
			log.debug("poiMgmt initialized:" + poiManager);
		}
	}

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		log.debug("doGet() called"); 
		String command = request.getParameter(COMMAND_PARAM);
		log.debug("command=" + command);
		try {
			if (command != null) {
				GetHandler handler = getHandlers.get(command);
				if (handler != null) {
					handler.handle(request, response);
				} else {
					RequestDispatcher rd = getServletContext()
							.getRequestDispatcher(UNKNOWN_COMMAND_URL);
					rd.forward(request, response);
				}
			} else {
				throw new Exception("no " + COMMAND_PARAM + " supplied");
			}
		} catch (Exception ex) {
			log.error("error within GET", ex);
			request.setAttribute(EXCEPTION_PARAM, ex);
			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					UNKNOWN_COMMAND_URL);
			rd.forward(request, response);
		}
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		log.debug("doPost() called");
		String command = request.getParameter(COMMAND_PARAM);
		log.debug("command=" + command);
		try {
			if (command != null) {
				PostHandler handler = postHandlers.get(command);
				if (handler != null) {
					handler.handle(request, response);
				} else {
					log.debug("No post handler, calling doGet() instead");
					doGet(request, response);
				}
			} else {
				throw new Exception("no " + COMMAND_PARAM + " supplied");
			}
		} catch (Exception ex) {
			log.error("error within POST", ex);
			request.setAttribute(EXCEPTION_PARAM, ex);
			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					UNKNOWN_COMMAND_URL);
			rd.forward(request, response);
		}
		//doGet(request, response);
	}

	public void destroy() {
		log.debug("destroy() called");
	}

	private abstract class PostHandler {
		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;
		
		protected static final String ID_PARAM = "id";
		protected static final String FIRST_NAME_PARAM = "firstName";
		protected static final String MIDDLE_NAME_PARAM = "middleName";
		protected static final String LAST_NAME_PARAM = "lastName";
		protected static final String HEIGHT_PARAM = "height";
		protected static final String WEIGHT_PARAM = "weight";
		protected static final String GENDER_PARAM = "gender";
		protected static final String ACT_DATE_PARAM = "actDate";
		protected static final String DESCRIPTION_PARAM = "description";
		protected static final String TYPE_PARAM = "type";

		protected static final String CREATE_POI_URL = "/WEB-INF/mayberry/CreatePOI.jsp";

		protected String action;

		public void handle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException {
			try {
				doHandle(request, response);
			} catch (Exception ex) {
				log.error("error in " + action, ex);
				request.setAttribute(EXCEPTION_PARAM, ex);
				RequestDispatcher rd = getServletContext()
						.getRequestDispatcher(DISPLAY_EXCEPTION_URL);
				rd.forward(request, response);
			}
		}

		public abstract void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception;
	}

	private abstract class GetHandler {
		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		protected static final String ID_PARAM = "id";
		protected static final String INDEX_PARAM = "index";
		protected static final String COUNT_PARAM = "count";
		protected static final String RESULT_PARAM = "result";
		protected static final String EXCEPTION_PARAM = "exception";
		protected static final String NEXT_INDEX_PARAM = "nextIndex";
		protected static final String SAMPLE_IDS_PARAM = "sampleIds";

		protected static final String DISPLAY_POIS_URL = "/WEB-INF/mayberry/DisplayPOIs.jsp";
		protected static final String DISPLAY_POI_URL = "/WEB-INF/mayberry/DisplayPOI.jsp";
		protected static final String CREATE_POI_URL = "/WEB-INF/mayberry/CreatePOI.jsp";
		protected static final String RETRIEVE_POI_URL = "/WEB-INF/mayberry/RetrievePOIs.jsp";
		protected static final String ADD_ACT_URL = "/WEB-INF/mayberry/AddActivity.jsp";
		protected static final String CHECK_DANGER_URL = "/WEB-INF/mayberry/CheckDanger.jsp";
		protected static final String ADD_DMV_PEEP_URL = "/WEB-INF/mayberry/AddDMVPOI.jsp";

		protected String action;

		public void handle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException {
			try {
				doHandle(request, response);
			} catch (Exception ex) {
				log.error("error in " + action, ex);
				request.setAttribute(EXCEPTION_PARAM, ex);
				RequestDispatcher rd = getServletContext()
						.getRequestDispatcher(DISPLAY_EXCEPTION_URL);
				rd.forward(request, response);
			}
		}

		public abstract void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception;
	}

	private class GetAllPOIs extends GetHandler {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			action = "EJB.getAllPOIs"; // describe action in case of exception

			String indexStr = (String) request.getParameter(INDEX_PARAM);
			String countStr = (String) request.getParameter(COUNT_PARAM);
			int index = Integer.parseInt(indexStr);
			int count = Integer.parseInt(countStr);

			List<POIDTO> people = poiManager.getAllPOI();
			List<POIDTO> results = new ArrayList<POIDTO>();

			for (int i = index; i < index + count; i++) {
				try {
					POIDTO newPeep = people.get(i);

					results.add(newPeep);
				} catch (IndexOutOfBoundsException aoe) {
					log.debug("Reached the end of the list.");
					break;
				}
			}
			int nextIndex = (results.size() == 0) ? index : index
					+ results.size();
			count = (results.size() >= count) ? count : results.size();
			request.setAttribute(RESULT_PARAM, results);
			request.setAttribute(INDEX_PARAM, index);
			request.setAttribute(COUNT_PARAM, count);
			request.setAttribute(NEXT_INDEX_PARAM, nextIndex);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_POIS_URL);
			rd.forward(request, response);

		}

	}

	private class CreatePOI extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			List<String> types = new ArrayList<String>();
			for (Type t : Type.values()) {
				types.add(t.name());
			}
			request.setAttribute("types", types);
			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					CREATE_POI_URL);
			rd.forward(request, response);
		}

	}

	private class CreatePOIPost extends PostHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			DateFormat dateParser = new SimpleDateFormat("MM/dd/yyyy");

			POIDTO newPeep = new POIDTO();
			newPeep.setFirstName(request.getParameter(FIRST_NAME_PARAM));
			newPeep.setMiddleName(request.getParameter(MIDDLE_NAME_PARAM));
			newPeep.setLastName(request.getParameter(LAST_NAME_PARAM));
			newPeep.setHeight(Integer.parseInt(request
					.getParameter(HEIGHT_PARAM)));
			newPeep.setWeight(Integer.parseInt(request
					.getParameter(WEIGHT_PARAM)));
			newPeep.setGender(Sex.translate(request.getParameter(GENDER_PARAM)));
			ActivityDTO act = new ActivityDTO();
			act.setDate(dateParser.parse(request.getParameter(ACT_DATE_PARAM)));
			act.setDescription(request.getParameter(DESCRIPTION_PARAM));
			act.setType(Type.valueOf(request.getParameter(TYPE_PARAM)));
			newPeep.getActivities().add(act);

			poiManager.addPOI(newPeep);
			String result = "POI added successfully...";

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);

		}

	}

	private class RetrievePOIs extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			List<POIDTO> people = poiManager.getAllPOI();

			List<POIDTO> samples = new ArrayList<POIDTO>();

			int number = (people.size() >= 10) ? 10 : people.size();

			for(int i = 0; i < number; i++) {
				samples.add(people.get(i));
			}
			
			request.setAttribute(SAMPLE_IDS_PARAM, samples);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					RETRIEVE_POI_URL);
			rd.forward(request, response);
		}

	}
	
	private class AddAct extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			List<POIDTO> people = poiManager.getAllPOI();

			List<POIDTO> samples = new ArrayList<POIDTO>();

			int number = (people.size() >= 10) ? 10 : people.size();

			for(int i = 0; i < number; i++) {
				samples.add(people.get(i));
			}
			
			request.setAttribute(SAMPLE_IDS_PARAM, samples);
			
			List<String> types = new ArrayList<String>();
			for (Type t : Type.values()) {
				types.add(t.name());
			}
			request.setAttribute("types", types);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					ADD_ACT_URL);
			rd.forward(request, response);
		}

	}
	
	private class AddActPost extends PostHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			DateFormat dateParser = new SimpleDateFormat("MM/dd/yyyy");

			ActivityDTO act = new ActivityDTO();
			act.setDate(dateParser.parse(request.getParameter(ACT_DATE_PARAM)));
			act.setDescription(request.getParameter(DESCRIPTION_PARAM));
			act.setType(Type.valueOf(request.getParameter(TYPE_PARAM)));

			poiManager.addActivity(Long.parseLong(request.getParameter(ID_PARAM)), act);
			String result = "Activity added successfully...";

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);

		}

	}
	
	private class AddDmvPeep extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			List<PersonDTO> people = personManager.getAllPeople(); 
 
			List<PersonDTO> samples = new ArrayList<PersonDTO>();

			int number = (people.size() >= 10) ? 10 : people.size();

			for(int i = 0; i < number; i++) {
				samples.add(people.get(i));
			}
			
			request.setAttribute(SAMPLE_IDS_PARAM, samples);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					ADD_DMV_PEEP_URL);
			rd.forward(request, response);
		}

	}
	
	private class AddDmvPeepPost extends PostHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			

			poiManager.addPOIFromDMV(Long.parseLong(request.getParameter(ID_PARAM)));
			String result = "POI added successfully from DMV...";

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);

		}

	}
	
	private class CheckDanger extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			List<POIDTO> people = poiManager.getAllPOI();

			List<POIDTO> samples = new ArrayList<POIDTO>();

			int number = (people.size() >= 10) ? 10 : people.size();

			for(int i = 0; i < number; i++) {
				samples.add(people.get(i));
			}
			
			request.setAttribute(SAMPLE_IDS_PARAM, samples);
			
			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					CHECK_DANGER_URL);
			rd.forward(request, response);
		}

	}
	
	private class CheckDangerPost extends PostHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {

			
			POIDTO badGuy = poiManager.getPOI(Long.parseLong(request.getParameter(ID_PARAM)));
			boolean danger =  poiManager.isDangerous(badGuy);
			
			
			String result = "";
			if(danger) {
				result = "CRAP! That person is DANGEROUS! I wouldn't hang out with that POI if i was you.";
			} else {
				result = "Everything is fine.  That person is not dangerous.  Hang out without worry.";
			}

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);

		}

	}

	private class GetPOI extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			action = "EJB.getDriver"; // describe action in case of exception

			String idStr = (String) request.getParameter(ID_PARAM);
			long id = Long.parseLong(idStr);

			POIDTO person = poiManager.getPOI(id);

			request.setAttribute(RESULT_PARAM, person);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_POI_URL);
			rd.forward(request, response);
		}

	}

	private class ResetDB extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			action = "EJB.resetDB"; // describe action in case of exception

			testUtil.resetAll();
			dmvTestUtil.resetAll();

			String result = "Reset Successful...";

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);
		}

	}

	private class PopulateDB extends GetHandler {

		/**
    	 * 
    	 */
		private static final long serialVersionUID = 1L;

		@Override
		public void doHandle(HttpServletRequest request,
				HttpServletResponse response) throws ServletException,
				IOException, Exception {
			action = "EJB.populate"; // describe action in case of exception

			for (int i = 0; i < 100; i++) {

				POIDTO newPeep = new POIDTO();
				newPeep.setFirstName("Person-" + i);
				newPeep.setMiddleName("Danger-" + i);
				newPeep.setLastName("McPerson-" + i);
				newPeep.setHeight((i * 27) % 200);
				newPeep.setWeight((i * 17) % 400);
				if (i % 2 == 0) {
					newPeep.setGender(Sex.MALE);
				} else {
					newPeep.setGender(Sex.FEMALE);
				}
				ActivityDTO act = new ActivityDTO();
				act.setDate(new Date(System.currentTimeMillis()));
				String desc;
				if (i % 76 == 0) {
					desc = "This dude did some CRAZY stuff.  Very dangerous if you ask me";
				} else {
					desc = "This act was weak.  The person wouldnt hurt a fly.  Major chicken";
				}
				act.setDescription(desc);
				int type = i % Type.values().length;
				Type newType;
				switch (type) {
				case 0:
					newType = Type.ASSAULT;
					break;
				case 1:
					newType = Type.CALL;
					break;
				case 2:
					newType = Type.GTA;
					break;
				case 3:
					newType = Type.ROBBERY;
					break;
				case 4:
					newType = Type.TRAFFICSTOP;
					break;
				default:
					newType = Type.UNKNOWN;
					break;

				}
				act.setType(newType);
				newPeep.getActivities().add(act);
				poiManager.addPOI(newPeep);
			}

			dmvTestUtil.populate();
			String result = "Populate Successful...";

			request.setAttribute(RESULT_PARAM, result);

			RequestDispatcher rd = getServletContext().getRequestDispatcher(
					DISPLAY_RESULT_URL);
			rd.forward(request, response);
		}

	}

}
